#include "Sphere.h"

namespace D3D10Shapes
{
	Sphere::Sphere()
    	: mRadius(0.0f) 
  		, mNumSlices(0)
  		, mNumStacks(0)
		, mNumVertices(0)
		, mNumFaces(0)
		, mDevice(0)
		, mVertexBuffer(0)
		, mIndexBuffer(0)
	{
	
	}

	Sphere::~Sphere()
	{
		D3D10Utils::ReleaseCOM(mVertexBuffer);
		D3D10Utils::ReleaseCOM(mIndexBuffer);
	}

	void Sphere::init(ID3D10Device *device, const float radius, const unsigned __int32 numSlices, const unsigned __int32 numStacks)
	{
		mDevice = device;

		mRadius = radius;
		mNumSlices = numSlices;
		mNumStacks = numStacks;

		std::vector<D3D10Utils::Vertex> vertices;
		std::vector<unsigned __int32> indices;

		initStacks(vertices, indices);
	
		mNumVertices = vertices.size();
		mNumFaces = indices.size() / 3;

		// Create vertex buffer
		D3D10_BUFFER_DESC bufferDesc;
    	bufferDesc.Usage = D3D10_USAGE_IMMUTABLE;
    	bufferDesc.ByteWidth = sizeof(D3D10Utils::Vertex) * mNumVertices;
    	bufferDesc.BindFlags = D3D10_BIND_VERTEX_BUFFER;
    	bufferDesc.CPUAccessFlags = 0;
    	bufferDesc.MiscFlags = 0;
		
		D3D10_SUBRESOURCE_DATA initData;
    	initData.pSysMem = &vertices[0];
    	HRESULT hr = mDevice->CreateBuffer(&bufferDesc, &initData, &mVertexBuffer);
    	D3D10Utils::ErrorHandler(hr);

		// Create index buffer
    	bufferDesc.Usage = D3D10_USAGE_IMMUTABLE;
    	bufferDesc.ByteWidth = sizeof(DWORD) * mNumFaces*3;
    	bufferDesc.BindFlags = D3D10_BIND_INDEX_BUFFER;
    	bufferDesc.CPUAccessFlags = 0;
    	bufferDesc.MiscFlags = 0;
    	
    	initData.pSysMem = &indices[0];
    	hr = mDevice->CreateBuffer(&bufferDesc, &initData, &mIndexBuffer);
    	D3D10Utils::ErrorHandler(hr);
	}

	void Sphere::draw()
	{
		unsigned __int32 stride = sizeof(D3D10Utils::Vertex);
		unsigned __int32 offset = 0;
		mDevice->IASetVertexBuffers(0, 1, &mVertexBuffer, &stride, &offset);
		mDevice->IASetIndexBuffer(mIndexBuffer, DXGI_FORMAT_R32_UINT, 0);
		mDevice->DrawIndexed(mNumFaces * 3, 0, 0);
	}

	void Sphere::initStacks(VertexList& vertices, IndexList& indices)
	{
		// do not count the poles as rings
		unsigned __int32 numRings = mNumStacks - 1;

		// Clear and reserve enough space.
		vertices.clear();
		vertices.reserve(( (numRings) * (mNumSlices + 1) ) + 2);
		indices.clear();
		indices.reserve( (6 * (mNumStacks - 1) * (mNumSlices + 1)) + (3 * (mNumSlices + 1) * 2));

		const float phiStep = D3D10Utils::PI / mNumStacks;

		// Compute vertices for each stack ring.
		for(unsigned __int32 i = 1; i <= numRings; ++i)
		{
			const float phi = i * phiStep;

			// vertices of ring
			const float thetaStep = 2.0f * D3D10Utils::PI / mNumSlices;
			for(unsigned __int32 j = 0; j <= mNumSlices; ++j)
			{
				const float theta = j * thetaStep;

				D3D10Utils::Vertex vertex;

				// spherical to cartesian
				vertex.mPosition.x = mRadius * sinf(phi) * cosf(theta);
				vertex.mPosition.y = mRadius * cosf(phi);
				vertex.mPosition.z = mRadius * sinf(phi) * sinf(theta);

				D3DXVec3Normalize(&vertex.mNormal, &vertex.mPosition);

				vertex.mTexCoord.x = theta / (2.0f * D3D10Utils::PI);
				vertex.mTexCoord.y = phi / D3D10Utils::PI;

				vertices.push_back(vertex);
			}
		}

		// poles: note that there will be texture coordinate distortion
		vertices.push_back( D3D10Utils::Vertex(0.0f, -mRadius, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f) );
		vertices.push_back( D3D10Utils::Vertex(0.0f, mRadius, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f) );

		unsigned __int32 northPoleIndex = vertices.size() - 1;
		unsigned __int32 southPoleIndex = vertices.size() - 2;

		unsigned __int32 numRingVertices = mNumSlices + 1;

		// Compute indices for inner stacks (not connected to poles).
		for(unsigned __int32 i = 0; i < mNumStacks - 2; ++i)
		{
			for(unsigned __int32 j = 0; j < mNumSlices; ++j)
			{
				indices.push_back(i * numRingVertices + j);
				indices.push_back(i * numRingVertices + j+1);
				indices.push_back((i + 1) * numRingVertices + j);

				indices.push_back((i + 1) * numRingVertices + j);
				indices.push_back(i * numRingVertices + j + 1);
				indices.push_back((i + 1) * numRingVertices + j + 1);
			}
		}

		// Compute indices for top stack.  The top stack was written 
		// first to the vertex buffer.
		for(unsigned __int32 i = 0; i < mNumSlices; ++i)
		{
			indices.push_back(northPoleIndex);
			indices.push_back(i + 1);
			indices.push_back(i);
		}

		// Compute indices for bottom stack.  The bottom stack was written
		// last to the vertex buffer, so we need to offset to the index
		// of first vertex in the last ring.
		unsigned __int32 baseIndex = (numRings - 1) * numRingVertices;
		for(unsigned __int32 i = 0; i < mNumSlices; ++i)
		{
			indices.push_back(southPoleIndex);
			indices.push_back(baseIndex + i);
			indices.push_back(baseIndex + i + 1);
		}	
	}
}
  
